#pragma once
/***********************************************************************************************
COPYRIGHT 2011 Mafahir Fairoze

This file is part of Neural++.
(Project Website : http://mafahir.wordpress.com/projects/neuralplusplus)

Neural++ is a free software. You can redistribute it and/or modify it under the terms of
the GNU General Public License as published by the Free Software Foundation, either version 3
of the License, or (at your option) any later version.

Neural++ is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License <http://www.gnu.org/licenses/> for more details.

***********************************************************************************************/

#include <vector>
#include "Layer.h"
#include "Connector.h"
#include "AbstractFunction.h"
#include "TrainingSet.h"
#include "TrainingSample.h"
#include "TrainingMethod.h"
#include "TrainingEpochEventArgs.h"
#include "TrainingSampleEventArgs.h"

namespace NeuralPlusPlus
	{
	namespace Core        
		{
		/// <summary>
		/// <para>
		/// An abstract base class to represent a neural network. A typical neural network consists of a
		/// set of <see cref="ILayer"/>s acyclically interconnected by various <see cref="IConnector"/>s.
		/// Input layer gets the input from the user and network output is obtained from the output layer.
		/// </para>
		/// <para>
		/// To create a neural network, follow these steps
		/// <list type="bullet">
		/// <item>Create and customize layers</item>
		/// <item>Establish connections between layers (No cycles should exist)</item>
		/// <item>Construct Network specifying the desired input and output layers</item>
		/// </list>
		/// </para>
		/// <para>
		/// There are two modes in which a neural network can be trained. In 'Batch Training', the neural
		/// network is allowed to learn by specifying a predefined training set containing various training
		/// samples. In 'Online training mode', a random training sample is generated every time (usually
		/// by another neural network, called 'teacher' network) and is used for training. Both modes are
		/// supported by overloaded <c>Learn()</c> methods. <c>Run()</c> method is used to run a neural
		/// network against a particular input.
		/// </para>
		/// </summary>
		class Network abstract
			{
			public: Layer *inputLayer;
			public: Layer *outputLayer;
			public: std::vector<Layer*> layers;
			public: std::vector<Connector*> connectors;
			public: TrainingMethodType trainingMethod;

					/// <summary>
					/// This __event is invoked during the commencement of a new training iteration during 'Batch
					/// training' mode.
					/// </summary>
			public: __event void BeginEpochEvent(TrainingEpochEventArgs args);

					/// <summary>
					/// This __event is invoked whenever the network is about to learn a training sample.
					/// </summary>
			public: __event void BeginSampleEvent(TrainingSampleEventArgs args);

					/// <summary>
					/// This __event is invoked whenever the network has successfully completed learning a training
					/// sample.
					/// </summary>
			public: __event void EndSampleEvent(TrainingSampleEventArgs args);

					/// <summary>
					/// This __event is invoked whenever a training iteration is successfully completed during 'Batch
					/// training' mode.
					/// </summary>
			public: __event void EndEpochEvent(TrainingEpochEventArgs args);

					/// <summary>
					/// Epoch(interval) at which Jitter operation is performed. If this->value is zero, not jitter is
					/// performed.
					/// </summary>
			public: int jitterEpoch;

					/// <summary>
					/// Maximum absolute limit to the random noise added during Jitter operation
					/// </summary>
			public: double jitterNoiseLimit;

					/// <summary>
					/// This flag is set to true, whenever training needs to be stopped immmediately
					/// </summary>
			public: bool isStopping;

					/// <summary>
					/// Layer Indexer
					/// </summary>
			public: Layer* operator[](int index);

					/// <summary>
					/// Creates a new neural network
					/// </summary>
			protected: Network(Layer *inputLayer, Layer *outputLayer, TrainingMethodType trainingMethod);

					/// <summary>
					/// Invokes BeginEpochEvent
					/// </summary>
			protected: virtual void OnBeginEpoch(int currentIteration, TrainingSet *trainingSet);

					   /// <summary>
					   /// Invokes EndEpochEvent
					   /// </summary>
			protected: virtual void OnEndEpoch(int currentIteration, TrainingSet *trainingSet);

					   /// <summary>
					   /// Invokes BeginSampleEvent
					   /// </summary>
			protected: virtual void OnBeginSample(int currentIteration, TrainingSample *currentSample);

					   /// <summary>
					   /// Invokes BeginSampleEvent
					   /// </summary>
			protected: virtual void OnEndSample(int currentIteration, TrainingSample *currentSample);

					   /// <summary>
					   /// Sets the learning rate to the given value. All layers in the network will use this->constant
					   /// value as learning rate during the learning process.
					   /// </summary>
			public: void SetLearningRate(double learningRate);

					/// <summary>
					/// Sets the initial and final values for learning rate. During the learning process, all
					/// layers in the network will use an efeective learning rate which varies uniformly from
					/// the initial value to the final value.
					/// </summary>
			public: void SetLearningRate(double initialLearningRate, double finalLearningRate);

					/// <summary>
					/// Sets the learning rate function.
					/// </summary>
			public: void SetLearningRate(AbstractFunction *learningRateFunction);

					/// <summary>
					/// Initializes all layers and connectors and makes them ready to undergo fresh training.
					/// </summary>
			public: virtual void Initialize();

					/// <summary>
					/// Runs the neural network against the given input
					/// </summary>
			public: virtual double* Run(double* input);

					/// <summary>
					/// Trains the neural network for the given training set (Batch Training)
					/// </summary>
			public: virtual void Learn(TrainingSet *trainingSet, int trainingEpochs);

					/// <summary>
					/// Trains the network for the given training sample (Online training mode). Note that this->
					/// method trains the sample only once, irrespective of what current epoch is. The arguments
					/// are just used to find out training progress and adjust parameters depending on it.
					/// </summary>
			public: virtual void Learn(TrainingSample *trainingSample, int currentIteration, int trainingEpochs);

					/// <summary>
					/// If the network is currently learning, this->method stops the learning.
					/// </summary>
			public: void StopLearning();

					/// <summary>
					/// A protected: helper function used to train single learning sample
					/// </summary>
			protected: virtual void LearnSample(TrainingSample *trainingSample, int currentIteration, int trainingEpochs) = 0;
			};
		}    
	}